#include "codecounterthread.h"
#include <QDebug>
#include <QDir>
#include <QFileInfo>

CodeCounterThread::CodeCounterThread(QObject* parent)
    : QThread(parent)
    , mWorker(new CodeCounterWorker(this))
{
    mWorker->moveToThread(this);
}

CodeCounterThread::~CodeCounterThread()
{

}

void CodeCounterThread::setLangTypes(const QList<LangType>& langTypes)
{
    mLangTypes = langTypes;
}

void CodeCounterThread::setSourceDirectory(const QString& sourceDir)
{
    mSourceDir = sourceDir;
}

void CodeCounterThread::setContainsSubDir(bool containsSubDir)
{
    mContainsSubDir = containsSubDir;
}

CodeCounterWorker* CodeCounterThread::worker()
{
    return mWorker;
}

CodeCounterWorker::CodeCounterWorker(CodeCounterThread* thread)
    : mThread(thread)
{
    qRegisterMetaType<CountResult>("CountResult");
    qRegisterMetaType<FileCountResult>("FileCountResult");
}

void CodeCounterWorker::startCount()
{
    mUserStop = false;
    emit started();

    mFileInfoList.clear();
    mCountResult = CountResult();

    QDir sourceDir(mThread->mSourceDir);
    QFileInfoList fileInfoList = sourceDir.entryInfoList(
                QDir::AllEntries | QDir::NoDotAndDotDot);
    foreach (const QFileInfo& fileInfo, fileInfoList)
    {
        if (fileInfo.isFile())
            mFileInfoList.append(fileInfo);
        else if (mThread->mContainsSubDir && fileInfo.isDir())
            mFileInfoList.append(fileInfo);
    }

    QMetaObject::invokeMethod(this, "process", Qt::QueuedConnection);
}

void CodeCounterWorker::stopCount()
{
    mFileInfoList.clear();
    mUserStop = true;
}

void CodeCounterWorker::process()
{
    if (mFileInfoList.isEmpty())
    {
        emit finished(mCountResult, mUserStop);
        return;
    }

    QFileInfo fileInfo = mFileInfoList.takeFirst();
    if (fileInfo.isFile())
    {
        processFile(fileInfo);
    }
    else if (fileInfo.isDir())
    {
        QDir dir = fileInfo.absoluteFilePath();
        mFileInfoList.append(
                    dir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot));
    }

    QMetaObject::invokeMethod(this, "process", Qt::QueuedConnection);
}

void CodeCounterWorker::processFile(const QFileInfo& fileInfo)
{
    QString ext = fileInfo.suffix();
    if (ext.isEmpty())
        return;

    ext = "." + ext;

    FileCountResult result;

    for (int i=0; i<mThread->mLangTypes.size(); ++i)
    {
        if (mThread->mLangTypes[i].fileExtensions.contains(ext))
        {
            QString single     = mThread->mLangTypes[i].singleLineComment;
            QString multiBegin = mThread->mLangTypes[i].multiLineCommentsBegin;
            QString multiEnd   = mThread->mLangTypes[i].multiLineCommentsEnd;

            QFile file(fileInfo.absoluteFilePath());
            if (!file.open(QIODevice::Text | QIODevice::ReadOnly))
            {
                qDebug() << "open file failed" << fileInfo.absoluteFilePath();
                break;
            }

            while (!file.atEnd())
            {
                 QString line = file.readLine().trimmed();
                 if (line.isEmpty())
                 {
                     result.countInfo.blankLines += 1;
                 }
                 else if (!single.isEmpty() && line.startsWith(single))
                 {
                     result.countInfo.commentLines += 1;
                 }
                 else
                 {
                     if (multiBegin.isEmpty() || multiEnd.isEmpty())
                     {
                         result.countInfo.codeLines += 1;
                         continue;
                     }

                     // 处理多行注释
                     int posMultiBegin = line.indexOf(multiBegin);

                     if (posMultiBegin != 0)
                         result.countInfo.codeLines += 1;
                     else if (posMultiBegin == 0)
                         result.countInfo.commentLines += 1;

                     if (posMultiBegin != -1)
                     {
                         if (!line.contains(multiEnd))
                         {
                             while (!file.atEnd())
                             {
                                 QString nextLine = file.readLine().trimmed();
                                 result.countInfo.commentLines += 1;

                                 if (nextLine.contains(multiEnd))
                                 {
                                     if (!nextLine.endsWith(multiEnd))
                                     {
                                         result.countInfo.codeLines += 1;
                                         result.countInfo.commentLines -= 1;
                                     }
                                     break;
                                 }
                             }
                         }
                     }
                 }
            }

            file.seek(file.size() - 1);
            QByteArray endChar = file.read(1);
            if (endChar == "\n")
                result.countInfo.blankLines += 1;

            QDir sourceDir(mThread->mSourceDir);
            QString relativeFilePath =
                    sourceDir.relativeFilePath(fileInfo.absoluteFilePath());

            result.fileName = fileInfo.fileName();
            result.path = relativeFilePath.mid(0, relativeFilePath.lastIndexOf('/') + 1);

            result.countInfo.totalLines = result.countInfo.blankLines +
                    result.countInfo.codeLines + result.countInfo.commentLines;

            mCountResult.blankLines   += result.countInfo.blankLines;
            mCountResult.codeLines    += result.countInfo.codeLines;
            mCountResult.commentLines += result.countInfo.commentLines;
            mCountResult.totalLines   += result.countInfo.totalLines;
            emit fileFinished(result, mCountResult);

            break;
        }
    }
}

